/*
* File: logging.c
* Author: Henrik Wistbacka
* --------------------------
* This file contains the implementation of logging
* network connections from a client to a server.
*
* The parameters that are logged is domain name, port name
* date, time, command, answer and number of sent bytes.
*/

/* DATELENGTH is the length of the array containing the 
* date */
#define DATELENGTH 11

/* TIMELENGTH is the length of the array
* containing the time. */
#define TIMELENGTH 9

/* HTMLTIMESTRINGLEN is the length of the full
* date and time string as it is expected in HTML
* response. */
#define HTMLTIMESTRINGLEN 36

#include "logging.h"
#include "config.h"
#include "files_and_reply.h"

#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>

#pragma comment(lib, "ws2_32.lib")

/* Type: initStatusT
* is used to keep track of if init has been done
* before any other function can be used. */
typedef enum {
	initDone = 0,
	initNotDone = 1
} initStatusT;

/* logfile is the handle to the logfile. */
static FILE *logfile = NULL;

/* initStatusT to keep track of if init has been done. */
static initStatusT initLogging = initNotDone;

/* logMutex to mutually exclude threads 
* from using some functions. */
static HANDLE logMutex = NULL;

/* fullLog is a pointer to the content
* of the full logfile. */
static char *fullLog = NULL;
static long sizeOfFullLog = 0;

/* bufferForLog is a pointer for the 
* temporary buffer created for the log. */
static char *bufferForLog = NULL;
static int bufferSize = 0;

/* logDateTime is a pointer to a string
* containing the date and time in one string.
* logDate is an array containing the date.
* logTime is an array containing the time. */
static char *logDateTime;
static char logDate[DATELENGTH];
static char logTime[TIMELENGTH];

/* HTMLTimeString is the full string with date and time as
* expected by HTTP clients. */
static char HTMLTimeString[HTMLTIMESTRINGLEN];

static char hostName[NI_MAXHOST];
static char portName[NI_MAXSERV];

/* logFormat is a format that is saved to disk. 
* It is on the same format as printf("%s"). */
static char *logFormat = "%-20s %-8s %-11s %-10s %-60s %-8s %10d\r\n";

/*Private procedures and functions.*/

static void setCurrentDate(logT log);
static void setCurrentTime(logT log);
static int sizeOfLog(logT log);
static errorT buildStringFromLog(logT log);
static errorT createBufferForLog(logT log);
static errorT freeOldReservNewLog();
static errorT readAllLogfile();
static void retrieveDateTime();

errorT initiliseLogging(const char * logfileName, const char *mode){
	
	logMutex = CreateMutex(NULL, FALSE, NULL);

	if (logMutex == NULL)
		return failedMutex;

	if (logfileName == NULL || strlen(logfileName) < 1)
		return filenameTooShort;

	if (strcmp("wb+", mode) == 0 || (strcmp("ab+", mode) == 0)){
		
		fopen_s(&logfile, logfileName, mode);

		if (logfile == NULL)
			return fileOpenError;
	}

	initLogging = initDone;
	return allOK;
}

errorT getFullLogFile(int *length, char **logMessage){

	DWORD waitAnswer = 0;
	long size = 0;
	errorT error;

	fullLog = NULL;
	*length = 0;
	*logMessage = NULL;

	if (initLogging == initNotDone)
 		return initNotPerformed;



	if ((error = freeOldReservNewLog()) != allOK)
		return error;

	waitAnswer = WaitForSingleObject(logMutex, INFINITE);

	if (waitAnswer == WAIT_OBJECT_0){

		if ((error = readAllLogfile()) == allOK){
			*logMessage = fullLog;
			*length = sizeOfFullLog;
		}
	}

	if(waitAnswer == WAIT_FAILED)
			error = failedMutex;

	ReleaseMutex(logMutex);

	return error;
}

errorT setDomainAndPortName(struct sockaddr clientAddr, int clientAddrLen, logT log){

	errorT errorCode;

	if (initLogging == initNotDone)
		return initNotPerformed;

	if ((errorCode = getnameinfo(&clientAddr, clientAddrLen, hostName, NI_MAXHOST, portName, NI_MAXSERV, NI_NUMERICSERV)) == allOK){
		log->domainName = hostName;
		log->portName = portName;
		return errorCode;
	}
	else
		return domainError;

	printf("Function %s not implemented yet.\n", __FUNCTION__);
}

errorT saveLogToFile(logT log){

	errorT error = allOK;
	DWORD waitAnswer = 0;
	char *p = NULL;
	bufferForLog = NULL;
	bufferSize = 0;

	int writtenChars = 0;

	if (initLogging == initNotDone)
		return initNotPerformed;

	waitAnswer = WaitForSingleObject(logMutex, INFINITE);

	if (waitAnswer == WAIT_OBJECT_0){

		fseek(logfile, 0, SEEK_END);

		retrieveDateTime();
		setCurrentDate(log);
		setCurrentTime(log);

		buildStringFromLog(log);

		for (p = bufferForLog; p < (bufferForLog + bufferSize); p += writtenChars)
			writtenChars = fwrite((void*)p, sizeof(char), bufferSize, logfile);

		if (ferror(logfile))
			error = fileError;
	}

	if (waitAnswer == WAIT_FAILED)
		error = failedMutex;

	ReleaseMutex(logMutex);

	return error;
}

errorT endLogging(){

	if (initLogging == initNotDone)
		return initNotPerformed;

	initLogging = initNotDone;

	if (bufferForLog != NULL)
		free(bufferForLog);

	if (fullLog != NULL)
		free(fullLog);

	CloseHandle(logMutex);

	fclose(logfile);

	return allOK;
}

errorT getHTMLTime(char **HTMLTime){

	retrieveDateTime();

	HTMLTimeString[0] = '\0';

	strcat(HTMLTimeString, "Date: ");
	strcat(HTMLTimeString, logDateTime);
	

	HTMLTimeString[9] = ',';
	HTMLTimeString[10] = ' ';
	HTMLTimeString[11] = logDateTime[8];
	HTMLTimeString[12] = logDateTime[9];
	HTMLTimeString[13] = logDateTime[10];
	HTMLTimeString[14] = logDateTime[4];
	HTMLTimeString[15] = logDateTime[5];
	HTMLTimeString[16] = logDateTime[6];
	HTMLTimeString[17] = ' ';
	HTMLTimeString[18] = logDateTime[20];
	HTMLTimeString[19] = logDateTime[21];
	HTMLTimeString[20] = logDateTime[22];
	HTMLTimeString[21] = logDateTime[23];
	HTMLTimeString[22] = ' ';
	HTMLTimeString[23] = logDateTime[11];
	HTMLTimeString[24] = logDateTime[12];
	HTMLTimeString[25] = logDateTime[13];
	HTMLTimeString[26] = logDateTime[14];
	HTMLTimeString[27] = logDateTime[15];
	HTMLTimeString[28] = logDateTime[16];
	HTMLTimeString[29] = logDateTime[17];
	HTMLTimeString[30] = logDateTime[18];
	HTMLTimeString[31] = ' ';
	HTMLTimeString[32] = 'G';
	HTMLTimeString[33] = 'M';
	HTMLTimeString[34] = 'T';
	HTMLTimeString[35] = '\0';

	*HTMLTime = HTMLTimeString;

	return allOK;
}

static void setCurrentDate(logT log){
	//server: //Date: Wed, 11 Feb 2015 19:28:29 GMT
	//ctime:        //Wed Feb 13 16:04:43 2002
	int i;

	for (i = 0; i < DATELENGTH; i++)
		logDate[i] = logDateTime[i];

	logDate[DATELENGTH - 1] = '\0';

	log->contactDate = logDate;

}

static void retrieveDateTime(){

	time_t timeNow;
	timeNow = time(&timeNow);

	/* Withdraw an hour to get real GMT time,
	* otherwise it would be local time in Sweden.*/
	timeNow = timeNow - 3600;

	logDateTime = ctime(&timeNow);

}

static void setCurrentTime(logT log){

	int i;

	for (i = 0; i < TIMELENGTH; i++)
		logTime[i] = logDateTime[i + 11];

	logTime[TIMELENGTH - 1] = '\0';

	log->contactTime = logTime;

}

static errorT freeOldReservNewLog(){
	
	errorT error = allOK;
	long size = 0;

	error = fileSize(&size, logfile);

	if (fullLog != NULL){
		free( (void *) fullLog);
		fullLog = NULL;
		sizeOfFullLog = 0;
	}

	if (size > 0){
		fullLog = (char *)malloc((size + 1) * sizeof(char));
		memset((void *)fullLog, '\0', (size + 1));
		sizeOfFullLog = size;
		error = allOK;
	}
	else
		error = logFileEmpty;

	return error;
}

static errorT readAllLogfile(){

	size_t readNumChars = 0;
	char *p;

	fseek(logfile, 0, SEEK_SET);

	for (p = fullLog; p < (fullLog + sizeOfFullLog); p += readNumChars)
		readNumChars = fread((void *)p, sizeof(char), sizeOfFullLog, logfile);

	fullLog[sizeOfFullLog] = '\0';

	if (ferror(logfile))
		return fileError;

	return allOK;
}

static int sizeOfLog(logT log){

		return _scprintf(logFormat,
		log->domainName,
		log->portName,
		log->contactDate,
		log->contactTime,
		log->HTTPCommand,
		log->serverHTTPAnswer,
		log->serverSentBytes);

}

static errorT buildStringFromLog(logT log){

	createBufferForLog(log);

	if (sprintf(bufferForLog, logFormat,
		log->domainName,
		log->portName,
		log->contactDate,
		log->contactTime,
		log->HTTPCommand,
		log->serverHTTPAnswer,
		log->serverSentBytes) < 0)
		return stringBuildError;

	return allOK;

}

static errorT createBufferForLog(logT log){

	errorT error = allOK;

	if (bufferForLog != NULL){
		free((void *)bufferForLog);
		bufferSize = 0;
		bufferForLog = NULL;
	}

	bufferSize = sizeOfLog(log);
	int bufferSizeInc_0 = bufferSize + 1;

	bufferForLog = (char *)malloc(bufferSizeInc_0);
	memset((void *)bufferForLog, '\0', bufferSizeInc_0);

	return error;
}